﻿/*
Copyright (c) 2010 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using TeachMe.Application.Forms;
using TeachMe.Application.Properties;

namespace TeachMe.Application
{
    public class WebContentDownloader<T, TZ> where T : class
    {
        public delegate void ResultCallback(Job[] result);

        public class Job
        {
            public string Link { get; set; }
            public T Result { get; set; }
            public TZ Context { get; set; }
        }

        private class DownloaderThreadWorkerData : ThreadWorkerData
        {
            public Root Root { get; set; }
            public Job[] Jobs { get; set; }
        }

        private static void ThreadWorker(object obj)
        {
            DownloaderThreadWorkerData worker = obj as DownloaderThreadWorkerData;
            if (worker == null)
            {
                throw new InvalidOperationException();
            }

            using (WebClient client = new WebClient())
            {
                // TODO: add .net version(s) to user agent

                client.Headers.Add("user-agent", String.Format("TeachMe/{0} ({1}; {2}; {3})", worker.Root.CurrentVersion, Environment.OSVersion.VersionString, Environment.OSVersion.Platform, worker.Root.UserSettings.Language.Value));

                try
                {
                    foreach (Job job in worker.Jobs)
                    {
                        Logging.Debug(typeof(OnlineVersion), "Opening link: {0}", job.Link);

                        worker.UpdateStatus(Resources.ConnectingToServer);

                        // TODO: timeout

                        using (Stream data = client.OpenRead(job.Link))
                        {
                            worker.UpdateStatus(Resources.ParsingResponse);

                            if (!worker.Canceled)
                            {
                                using (StreamReader reader = new StreamReader(data, Encoding.UTF8))
                                {
                                    if (typeof(T).Equals(typeof(string)))
                                    {
                                        // TODO: hack
                                        job.Result = reader.ReadToEnd() as T;
                                    }
                                    else
                                    {
                                        job.Result = Utilities.Deserialize<T>(reader.ReadToEnd());
                                    }
                                }
                            }
                        }
                    }
                }
                catch (WebException e)
                {
                    Logging.Error(typeof(OnlineVersion), e, "Could check new version availability.");
                }
            }

            worker.Exit();
        }

        public static void Download(Root root, PseudoModalForm parent, Job[] jobs, string caption, ResultCallback callback)
        {
            DownloaderThreadWorkerData worker = new DownloaderThreadWorkerData { Root = root, Jobs = jobs };

            BusyForm form = new BusyForm(worker) { WaitCaption = caption };
            root.FormsManager.Add(form);

            if (parent == null)
            {
                form.Center();
            }
            else
            {
                form.PseudoParent = parent;
            }

            form.Show();

            form.Closed += delegate
                               {
                                   root.FormsManager.Remove(form);

                                   if (!worker.Canceled)
                                   {
                                       callback(worker.Jobs);
                                   }
                               };

            Thread thread = new Thread(ThreadWorker) { CurrentUICulture = Thread.CurrentThread.CurrentUICulture };
            thread.Start(worker);
        }
    }
}
